package tr.edu.metu.is.mega.server.core;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.queries.QueryByExamplePolicy;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReportQuery;

/**
 * Implementation of entity search helper.
 * 
 * @param <T> type modifier
 */
public class EntitySearchHelper<T> {

    /**
     * @see DAO#findByExample(BaseEntity, boolean)
     */
    public List<T> findByExample(T example, EntityManager entityManager, boolean useLikeQueryForStringFields) {
        return findByExampleHelper(example, entityManager, useLikeQueryForStringFields, null, null);
    }

    /**
     * @see DAO#findByExample(BaseEntity, boolean, PagedQueryParams)
     */
    public Long findByExampleCount(T example, EntityManager entityManager, boolean useLikeQueryForStringFields) {
        return findByExampleCount(example, entityManager, useLikeQueryForStringFields, null);
    }

    /**
     * @see DAO#getCountByExample(BaseEntity, boolean, Expression)
     */
    public Long findByExampleCount(T example, EntityManager entityManager, boolean useLikeQueryForStringFields, Expression expr) {
        QueryByExamplePolicy policy = new QueryByExamplePolicy();
        policy.excludeDefaultPrimitiveValues();

        if (useLikeQueryForStringFields) {
            policy.addSpecialOperation(String.class, "like");
        }

        ReportQuery countQuery = new ReportQuery();
        countQuery.setQueryByExamplePolicy(policy);
        countQuery.setExampleObject(example);
        countQuery.addCount();
        countQuery.setShouldReturnSingleValue(true);

        if (expr != null) {
            countQuery.setSelectionCriteria(expr);
        }
        Query query = JpaHelper.createQuery(countQuery, entityManager);

        return Long.valueOf(query.getSingleResult().toString());
    }

    /**
     * @see DAO#findByExample(BaseEntity, boolean, PagedQueryParams, Expression)
     */
    public List<T> findByExample(T example, EntityManager entityManager, boolean useLikeQueryForStringFields, PagedQueryParams params, Expression expr) {
        return findByExampleHelper(example, entityManager, useLikeQueryForStringFields, params, expr);
    }

    /**
     * @see DAO#findByExample(BaseEntity, boolean, PagedQueryParams, Expression)
     */
    public List<T> findByExample(T example, EntityManager entityManager, boolean useLikeQueryForStringFields, PagedQueryParams params) {
        return findByExampleHelper(example, entityManager, useLikeQueryForStringFields, params, null);
    }

    /**
     * Finds entities by example instance.
     * 
     * @param example example instance
     * @param entityManager entity manager
     * @param useLike like comparison indicator
     * @param params paging parameters
     * @param expr expression
     * @return list of records
     */
    @SuppressWarnings("all")
    private List<T> findByExampleHelper(T example, EntityManager entityManager, boolean useLike, PagedQueryParams params, Expression expr) {
        QueryByExamplePolicy policy = new QueryByExamplePolicy();
        policy.excludeDefaultPrimitiveValues();

        if (useLike) {
            policy.addSpecialOperation(String.class, "like");
        }

        ReadAllQuery readAllQuery = new ReadAllQuery(example, policy);

        if (params != null) {
            if (params.getDirection() != null) {
                if (params.getDirection() == SortDirection.ASC) {
                    readAllQuery.addAscendingOrdering(params.getSortField());
                } else {
                    readAllQuery.addDescendingOrdering(params.getSortField());
                }
            }

            readAllQuery.setMaxRows(params.getMaxResults());
            readAllQuery.setFirstResult(params.getFirstResult());
        }

        if (expr != null) {
            readAllQuery.setSelectionCriteria(expr);
        }

        Query query = JpaHelper.createQuery(readAllQuery, entityManager);

        return (List<T>) query.getResultList();
    }

}
